package at.ac.tuwien.isis.infret.ue3;


import java.util.TreeMap;
import java.util.Vector;

import at.ac.tuwien.isis.infret.helper.DocumentContainer;
import at.ac.tuwien.isis.infret.ue3.interfaces.IArffParser;
import at.ac.tuwien.isis.infret.ue3.interfaces.IQueryHandler;

public class QueryHandler implements IQueryHandler {

	private Vector<DocumentContainer> dc;
	//takes "L2" and "Cosine"
	public String SimilarityFunctionFlag = "l2"; 
	private Vector<String> docNames = new Vector<String>();
	private Double[][] distanceMatrix;
	
	public QueryHandler(String arffPath){
		IArffParser parser = new ArffParser(arffPath);
		this.dc = parser.getDocumentContainer();
		
		//System.out.print("Computing global distance matrix");
		
		//System.out.print("..Done");
		
		for(DocumentContainer container: dc)
			docNames.add(container.toString());
	}
	
	public Vector<String> getDocNames(){
		return this.docNames;
	}
	
	public  Double[][] computeDistanceMatrix(){
		distanceMatrix = new Double[dc.size()][dc.size()];
		 
		for(int i = 0; i < dc.size(); i ++){
			for(int j = i; j < dc.size(); j++){
				if(i == j){
					distanceMatrix[i][j] = 0.0;
				}
				Double distance = this.applySimilarityFuntion(dc.get(i), dc.get(j));
				distanceMatrix[i][j] = distance;
				distanceMatrix[j][i] = distance; 
			}
		}
		return distanceMatrix;
	}
	
	@Override
	public TreeMap<DocumentContainer,Double> applyQuery(Vector<String> queryDocuments, int kBest) {
		TreeMap<DocumentContainer,Double> list = new TreeMap<DocumentContainer,Double>();
		
		Vector<DocumentContainer> queryContainer = this.getQueryContainer(queryDocuments);
		
		for (DocumentContainer document : dc){
			Double similarityValue = 0.0; 
			
			//accumulate similarity functions between entry in dc and all query documents
			for (DocumentContainer query : queryContainer){
				similarityValue += this.applySimilarityFuntion(document, query);
			}
			//normalize result
			similarityValue = similarityValue/queryContainer.size();
			//System.out.println(similarityValue);
			/*if(best.size()<kBest){
				//System.out.print("=");
				best.put(similarityValue, document);
			}else if(best.lastKey()> similarityValue){
				System.out.print("=");
				best.pollLastEntry();*/
			list.put(document,similarityValue);
			
			//}
			
		}
		
		return list;
	}
	
	public Vector<DocumentContainer> getQueryContainer(Vector<String> queryDocuments){

		Vector<DocumentContainer> queryContainer = new Vector<DocumentContainer>();
		for( String query: queryDocuments){
			String[] ident = query.split("/");
			//System.out.println(Arrays.toString(ident));
			DocumentContainer dummy = new DocumentContainer(ident[0], ident[1], null);
			queryContainer.add(dc.get(dc.indexOf(dummy)));
			dc.remove(dummy);
			//System.out.println(dc);
		}
		
		return queryContainer;
	}
	public Double applySimilarityFuntion(DocumentContainer left, DocumentContainer right){
		if( this.SimilarityFunctionFlag.equals("l2")){
			return applyL2SimilarityFunction(left, right);
		}
		if( this.SimilarityFunctionFlag.equals("cosine")){
			return applyCosineSimilarityFunction(left, right);
		}
		return null;
	}
	public Double applyL2SimilarityFunction(DocumentContainer left, DocumentContainer right){
		//first 2 attributes contain classname and documentname
		Double distance = 0.0;
		//System.out.println(right.getAttributes().toDoubleArray()[0]);
		//System.out.println(right.getAttributes().toDoubleArray()[1]);
		int i = 0;
		double[] leftArray = left.getAttributes().toDoubleArray();
		double[] rightArray = right.getAttributes().toDoubleArray();
		while(i < leftArray.length){
			distance += Math.pow(leftArray[i]-rightArray[i],2);
			i++;
		}		
		return Math.sqrt(distance);
	}
	public Double applyCosineSimilarityFunction(DocumentContainer left, DocumentContainer right){
		Double numerator = 0.0;
		Double denominatorLeft =0.0;
		Double denominatorRight =0.0;
		double[] leftArray = left.getAttributes().toDoubleArray();
		double[] rightArray = right.getAttributes().toDoubleArray();
		int i =2;
		while (i < leftArray.length){
			numerator += leftArray[i] *rightArray[i];
			denominatorLeft += Math.pow(leftArray[i] , 2); 
			denominatorRight += Math.pow(rightArray[i], 2);
			i++;
		}
		//System.out.print(".");
		if(	Double.isNaN((1- numerator / Math.sqrt(denominatorLeft * denominatorRight))))
			return 1.0;
		else return(1- numerator / Math.sqrt(denominatorLeft * denominatorRight));
		
	}
}
